/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：
* CreateTime：2020-04-05 20:23:51
* Version：1.0
* Modify Recorder：
*************************************************/

using UnityEngine;

namespace TinyToolKit.Sound
{
    public enum AudioItemType
    {
        Music,
        FxSound,
        UISound
    }

    /// <summary>
    /// 封装了AudioSource的属性，并实现淡入淡出效果
    /// </summary>
    public class AudioItem
    {
        private TSoundManager _soundManager;
        private static int _audioCounter;//记录音效的数量

        //AudioSource 属性设置
        private AudioClip _clip;
        private bool _loop;
        private bool _mute;
        private int _priority;
        private float _pitch;
        private float _stereoPan;
        private float _spatialBlend;
        private float _reverbZoneMix;
        private float _dopplerLevel;
        private float _spread;
        private AudioRolloffMode _rolloffMode;
        private float _max3DDistance;
        private float _min3DDistance;

        //音量设置
        private float _fadeTargetVolume;
        private float _initTargetVolume;
        private float _fadeInterpolater;//渐变插值
        private float _fadeStartVolume;

        private Transform _sourceTransform;
        public Transform Sourcetransform
        {
            get => _sourceTransform;
            set => _sourceTransform = value;
        }

        //判断是否在切换场景的时候不销毁
        public bool Persist { get; set; }

        //判断是否需要淡入淡出的效果
        public bool FadeInOut { get; private set; }

        public float FadeInSeconds { get; set; }

        public float FadeOutSeconds { get; set; }

        #region Audio的状态
        /// <summary>
        /// Audio的ID，唯一识别
        /// </summary>
        public int AudioID { get; private set; }

        /// <summary>
        /// Audio的类型
        /// </summary>
        public AudioItemType Type { get; private set; }

        /// <summary>
        /// 判断Audio是否正在播放
        /// </summary>
        public bool IsPlaying { get; private set; }

        /// <summary>
        /// 判断Audio是否暂停
        /// </summary>
        public bool Paused { get; private set; }

        /// <summary>
        /// 判断Audio是否已经暂停
        /// </summary>
        public bool Stopped { get; private set; }

        /// <summary>
        ///判断Audio是否被创建，并且至少更新一次
        /// </summary>
        public bool Activated { get; private set; }

        /// <summary>
        /// 判断当前是否被存储到音频池中
        /// </summary>
        public bool Pooled { get; set; }
        #endregion

        #region AudioSource的属性设置
        /// <summary>
        /// 负责Audio的Audio Source
        /// </summary>
        public AudioSource AudioSource { get; private set; }

        public AudioClip Clip
        {
            get => _clip;
            set
            {
                _clip = value;
                if (AudioSource != null)
                    AudioSource.clip = _clip;
            }
        }

        public bool Loop
        {
            get => _loop;
            set
            {
                _loop = value;
                if (AudioSource != null)
                    AudioSource.loop = _loop;
            }
        }

        public bool Mute
        {
            get => _mute;
            set
            {
                _mute = value;
                if (AudioSource != null)
                    AudioSource.mute = _mute;
            }
        }

        /// <summary>
        /// 设置Audio的优先级，范围：[0，1]
        /// </summary>
        public int Priority
        {
            get => _priority;
            set
            {
                _priority = Mathf.Clamp(value, 0, 256);

                if (AudioSource != null)
                    AudioSource.priority = _priority;
            }
        }

        /// <summary>
        /// 设置Audio的音量，范围：[0，1]
        /// </summary>
        public float AudioVolume { get; private set; }

        /// <summary>
        /// 设置Audio的Pitch，范围：[-3，3]
        /// </summary>
        public float Pitch
        {
            get => _pitch;
            set
            {
                _pitch = Mathf.Clamp(value, -3, 3);

                if (AudioSource != null)
                    AudioSource.pitch = _pitch;
            }
        }

        /// <summary>
        /// 设置Audio的StereoPan，范围：[-1，1]
        /// </summary>
        public float StereoPan
        {
            get => _stereoPan;
            set
            {
                _stereoPan = Mathf.Clamp(value, -1, 1);
                if (AudioSource != null)
                {
                    AudioSource.panStereo = _stereoPan;
                }
            }
        }

        /// <summary>
        ///  设置Audio的spatialBlend，范围：[0，1]，值为0则是完全2D，值为1则是完全3D
        /// </summary>
        public float SpatialBlend
        {
            get => _spatialBlend;
            set
            {
                _spatialBlend = Mathf.Clamp01(value);
                if (AudioSource != null)
                    AudioSource.spatialBlend = _spatialBlend;
            }
        }

        /// <summary>
        /// 设置Audio的音频混响区
        /// </summary>
        public float ReverbZoneMix
        {
            get => _reverbZoneMix;
            set
            {
                _reverbZoneMix = Mathf.Clamp(value, 0, 1.1f);
                if (AudioSource != null)
                {
                    AudioSource.reverbZoneMix = _reverbZoneMix;
                }
            }
        }

        public float DopplerLevel
        {
            get => _dopplerLevel;
            set
            {
                _dopplerLevel = Mathf.Clamp(value, 0, 5);
                if (AudioSource != null)
                {
                    AudioSource.dopplerLevel = _dopplerLevel;
                }
            }
        }

        public float Spread
        {
            get => _spread;
            set
            {
                _spread = Mathf.Clamp(value, 0, 360);
                if (AudioSource != null)
                {
                    AudioSource.spread = _spread;
                }
            }
        }

        public AudioRolloffMode RolloffMode
        {
            get => _rolloffMode;
            set
            {
                _rolloffMode = value;
                if (AudioSource != null)
                {
                    AudioSource.rolloffMode = _rolloffMode;
                }
            }
        }

        public float Max3DDistance
        {
            get => _max3DDistance;
            set
            {
                _max3DDistance = Mathf.Max(value, 0.01f);
                if (AudioSource != null)
                {
                    AudioSource.maxDistance = _max3DDistance;
                }
            }
        }

        public float Min3DDistance
        {
            get => _min3DDistance;
            set
            {
                _min3DDistance = Mathf.Max(value, 0);
                if (AudioSource != null)
                {
                    AudioSource.minDistance = _min3DDistance;
                }
            }
        }

        public float Progress
        {
            get
            {
                if (AudioSource != null)
                    return AudioSource.time / AudioSource.clip.length;
                return 0;
            }
        }
        #endregion

        #region Init
        public AudioItem(TSoundManager soundManager,AudioItemType audioType, AudioClip clip, bool loop, bool persist, float volume, Transform sourceTransform)
        {
            AudioID = _audioCounter;
            _audioCounter++;

            this._soundManager = soundManager;
            this.Type = audioType;
            this.Clip = clip;
            this.Loop = loop;
            this.Persist = persist;
            this.Sourcetransform = sourceTransform;

            this._fadeTargetVolume = volume;
            this._initTargetVolume = volume;

            AudioVolume = 0;
            Pooled = false;
            Mute = false;
            Priority = 128;
            Pitch = 1;
            StereoPan = 0;
            if (Sourcetransform != null && Sourcetransform != _soundManager.Target)
            {
                SpatialBlend = 1;
            }
            else
            {
                Sourcetransform = _soundManager.Target;
                SpatialBlend = 0;
            }
            ReverbZoneMix = 1;
            DopplerLevel = 1;
            Spread = 0;
            RolloffMode = AudioRolloffMode.Logarithmic;
            Min3DDistance = 1;
            Max3DDistance = 500;

            IsPlaying = false;
            Paused = false;
            Activated = false;
            FadeInOut = false;
        }

        public void ActiveFadeInOutEffect(float fadeInValue, float fadeOutValue)
        {
            this.FadeInOut = true;
            this.FadeInSeconds = fadeInValue;
            this.FadeOutSeconds = fadeOutValue;
        }

        public void DeactiveFadeInOutEffect() 
        {
            this.FadeInOut = false;
        }

        private void AddAudioSource()
        { 
            AudioSource = Sourcetransform.gameObject.AddComponent<AudioSource>();
            AudioSource.clip = Clip;
            AudioSource.loop = Loop;
            AudioSource.mute = Mute;
            AudioSource.priority = Priority;
            AudioSource.volume = AudioVolume;
            AudioSource.pitch = Pitch;
            AudioSource.panStereo = StereoPan;
            AudioSource.spatialBlend = SpatialBlend;
            AudioSource.reverbZoneMix = ReverbZoneMix;
            AudioSource.dopplerLevel = DopplerLevel;
            AudioSource.spread = Spread;
            AudioSource.rolloffMode = RolloffMode;
            AudioSource.minDistance = Min3DDistance;
            AudioSource.maxDistance = Max3DDistance;
        }
        #endregion

        #region Public Func
        public void Play()
        {
            Play(_initTargetVolume);
        }

        public void Play(float volume)
        {
            if (Pooled)
            {
                bool restoredFromPool = _soundManager.RestoreAudioFromPool(Type, AudioID);
                if (restoredFromPool == false) return;

                Pooled = false;
            }

            if (AudioSource == null) AddAudioSource();

            AudioVolume = 0;
            _fadeStartVolume = AudioVolume;
            _fadeTargetVolume = Mathf.Clamp01(volume);
            _fadeInterpolater = 0;
            
            AudioSource.Stop();
            AudioSource.Play();
            IsPlaying = true;
            Paused = false;
            Stopped = false;
        }

        public void Stop()
        {
            Stopped = true;
            Paused = false;
            IsPlaying = false;
            _fadeInterpolater = 0f;
            _fadeTargetVolume = 0f;

            if (FadeInOut)
                _fadeStartVolume = AudioVolume;
            else
                _fadeStartVolume = 0;
        }

        public void Pause()
        {
            Paused = true;
            if (AudioSource != null) AudioSource.Pause();
        }

        public void Resume()
        {
            Paused = false;
            IsPlaying = true;
            if (AudioSource != null) AudioSource.UnPause();
        }

        public void Set3DDistances(float min, float max)
        {
            Min3DDistance = min;
            Max3DDistance = max;
        }

        public void UpdateAudio()
        {
            if (AudioSource == null || IsPlaying == false) return;

            Activated = true;
            if (FadeInOut)
            {
                if (AudioVolume != _fadeTargetVolume)
                {
                    float fadeValue;
                    if (AudioVolume > _fadeTargetVolume)
                        fadeValue = FadeOutSeconds;
                    else
                        fadeValue = FadeInSeconds;

                    _fadeInterpolater += Time.deltaTime;
                    if (fadeValue > 0)
                        AudioVolume = Mathf.Lerp(_fadeStartVolume, _fadeTargetVolume, _fadeInterpolater / fadeValue);
                    if (Mathf.Abs(AudioVolume - _fadeTargetVolume) < 0.01f)
                        AudioVolume = _fadeTargetVolume;
                }
            }
            else
            {
                AudioVolume = _fadeTargetVolume;
            }
            
            switch (Type)
            {
                case AudioItemType.Music:
                    AudioSource.volume = AudioVolume * _soundManager.GlobalMusicVolume * _soundManager.GlobalVolume;
                    break;
                case AudioItemType.FxSound:
                    AudioSource.volume = AudioVolume * _soundManager.GlobalFXSoundsVolume * _soundManager.GlobalVolume;
                    break;
                case AudioItemType.UISound:
                    AudioSource.volume = AudioVolume * _soundManager.GlobalUISoundsVolume * _soundManager.GlobalVolume;
                    break;
            }

            if (AudioVolume < 0.01f && Stopped)
            {
                AudioSource.Stop();
                Stopped = false;
                IsPlaying = false;
                Paused = false;
            }

            if (AudioSource.isPlaying != IsPlaying)
            {
                IsPlaying = AudioSource.isPlaying;
            }
        }
        #endregion
    }
}

